/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.settings.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.AudioManager;
import android.provider.Settings;
import android.util.Log;
import java.util.List;
import java.util.ArrayList;
import androidx.preference.Preference;

import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.dashboard.DashboardFragment;
import com.android.mtksettingslib.bluetooth.CachedBluetoothDevice;
import com.android.mtksettingslib.bluetooth.LocalBluetoothProfile;
import com.android.mtksettingslib.bluetooth.LeAudioProfile;
import android.os.SystemProperties;

/**
 * Controller to maintain available media Bluetooth devices
 */
public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater
        implements Preference.OnPreferenceClickListener {

    private static final String TAG = "AvailableMediaBluetoothDeviceUpdater";
    private static final boolean DBG = /*Log.isLoggable(TAG, Log.DEBUG)*/true;

    private static final String PREF_KEY = "available_media_bt";
    private boolean mActivating = false;
    private final AudioManager mAudioManager;
    private final Context mContext;
    private int ADDRESS_LENGTH = 18;

    public AvailableMediaBluetoothDeviceUpdater(Context context, DashboardFragment fragment,
            DevicePreferenceCallback devicePreferenceCallback) {
        super(context, fragment, devicePreferenceCallback);
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mContext =  context;
    }

    @Override
    public void onAudioModeChanged() {
        forceUpdate();
    }

    @Override
    public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
        final int audioMode = mAudioManager.getMode();
        final int currentAudioProfile;

        if (audioMode == AudioManager.MODE_RINGTONE
                || audioMode == AudioManager.MODE_IN_CALL
                || audioMode == AudioManager.MODE_IN_COMMUNICATION) {
            // in phone call
            currentAudioProfile = BluetoothProfile.HEADSET;
        } else {
            // without phone call
            currentAudioProfile = BluetoothProfile.A2DP;
        }

        boolean isFilterMatched = false;
        if (isDeviceConnected(cachedDevice) && cachedDevice.getLeAudioStatus()
                == BluetoothDevice.LE_ADUIO_STATE_OFF) {
            if (DBG) {
                Log.d(TAG, "isFilterMatched() current audio profile : " + currentAudioProfile);
            }
            // If device is Hearing Aid, it is compatible with HFP and A2DP.
            // It would show in Available Devices group.
            if (cachedDevice.isConnectedHearingAidDevice()) {
                return true;
            }

            if (cachedDevice.isConnectedLeAudioDevice()) {
                return true;
            }

            // According to the current audio profile type,
            // this page will show the bluetooth device that have corresponding profile.
            // For example:
            // If current audio profile is a2dp, show the bluetooth device that have a2dp profile.
            // If current audio profile is headset,
            // show the bluetooth device that have headset profile.
            switch (currentAudioProfile) {
                case BluetoothProfile.A2DP:
                    isFilterMatched = cachedDevice.isConnectedA2dpDevice();
                    break;
                case BluetoothProfile.HEADSET:
                    isFilterMatched = cachedDevice.isConnectedHfpDevice();
                    break;
            }
            if (DBG) {
                Log.d(TAG, "isFilterMatched() device : " +
                        cachedDevice.getName() + ", isFilterMatched : " + isFilterMatched);
            }
        } else {
            if ((cachedDevice.getAddress()).equals(cachedDevice.findBrAddress())) {
                String leStr = cachedDevice.findLeAddress();
                String le1Str = "";
                String le2Str = "";
                if (leStr != null) {
                    String[] deviceInfo = leStr.split(";");
                    if (deviceInfo.length > 1) {
                        le1Str = deviceInfo[0];
                        le2Str = deviceInfo[1];
                    }
                }
                Log.d(TAG,"isFilterMatched,le1Str = " + le1Str + ",le2Str = " + le2Str);
                if (!("".equals(le1Str)) && !("".equals(le2Str))) {
                    BluetoothDevice le1Device = BluetoothAdapter.getDefaultAdapter()
                            .getRemoteDevice(le1Str);
                    BluetoothDevice le2Device = BluetoothAdapter.getDefaultAdapter()
                            .getRemoteDevice(le2Str);
                    LeAudioProfile le_audioProfile = mLocalManager.getProfileManager().
                            getLeAudioProfile();
                    if (le_audioProfile != null) {
                        boolean isLe1ProfileConnected = le_audioProfile.
                                getConnectionStatus(le1Device) == BluetoothProfile.STATE_CONNECTED;
                        boolean isLe2ProfileConnected = le_audioProfile.
                                getConnectionStatus(le2Device) == BluetoothProfile.STATE_CONNECTED;
                        Log.d(TAG,"isFilterMatched,isLe1ProfileConnected = " + isLe1ProfileConnected
                                + ",isLe2ProfileConnected = " + isLe2ProfileConnected);
                        isFilterMatched = isLe1ProfileConnected || isLe2ProfileConnected;

                    }
                }
            }
        }
        return isFilterMatched;
    }
    @Override
      public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
      if (activeDevice != null) {
          Log.d(TAG, "onActiveDeviceChanged() device: " + activeDevice.getName() +
                    ", bluetoothProfile: " + bluetoothProfile);
          if(activeDevice.isLeDevice() || isFilterMatched(activeDevice)) {
              mActivating = false;
          }
       }
      }

    @Override
    public boolean onPreferenceClick(Preference preference) {
        mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory());
        // add error handle before switch,check if any le audio device during bonding or connecting.
        checkLeAudioStatus();
        final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference)
               .getBluetoothDevice();
        boolean leAudioStatusOn = device.getLeAudioStatus() == BluetoothDevice.LE_ADUIO_STATE_ON;
        String leStr = device.findLeAddress();
        String le1Str = "";
        String le2Str = "";
        if (leStr != null) {
            String[] deviceInfo = leStr.split(";");
            if (deviceInfo.length > 1) {
                le1Str = deviceInfo[0];
                le2Str = deviceInfo[1];
            }
        }
        Log.d(TAG, "onPreferenceClick,le1Str = " + le1Str + ",le2Str = " + le2Str);
        //if any profile of current device is not active, switch will broadcast
        boolean isCurrentDeviceIsActive = true;
        if (!("".equals(le1Str)) && !("".equals(le2Str)) && leAudioStatusOn) {
            LeAudioProfile le_audioProfile = mLocalManager.getProfileManager().getLeAudioProfile();
            if (le_audioProfile != null) {
                List<BluetoothDevice> activeDevices = le_audioProfile.getActiveDevices();
                Log.d(TAG, "onPreferenceClick,activeDevices.size() = " + activeDevices.size());
                for (int i = 0; i < activeDevices.size(); i++) {
                    String address = activeDevices.get(i).getAddress();
                    Log.d(TAG, "address = " + address);
                    if (le1Str.equals(address) || le2Str.equals(address)) {
                        isCurrentDeviceIsActive = true;
                    }
                }
            }
        } else {
            for (LocalBluetoothProfile profile : device.getConnectableProfiles()) {
                int profileId = profile.getProfileId();
                if ((profileId == BluetoothProfile.A2DP /*&& isLeAudioDeviceActive == false*/)
                        || profileId == BluetoothProfile.HEADSET
                        || profileId == BluetoothProfile.HEARING_AID) {
                    isCurrentDeviceIsActive = device.isActiveDevice(profileId);
                }
                if (!isCurrentDeviceIsActive) break;
            }
        }
        Log.d(TAG, "onPreferenceClick,isCurrentDeviceIsActive = " + isCurrentDeviceIsActive +
                ",mActivating = " + mActivating);

        if(!mActivating){
            if (!("".equals(le1Str)) && !("".equals(le2Str)) && leAudioStatusOn) {
                //FIXME,figure out which device should set active first.
                BluetoothDevice le1Device = BluetoothAdapter.getDefaultAdapter()
                        .getRemoteDevice(le1Str);
                BluetoothDevice le2Device = BluetoothAdapter.getDefaultAdapter()
                        .getRemoteDevice(le2Str);
                boolean isSetActiveDeviceCalled = false;
                LeAudioProfile le_audioProfile = mLocalManager.getProfileManager().
                        getLeAudioProfile();
                if (le_audioProfile != null) {
                    if (le_audioProfile.getConnectionStatus(le1Device) ==
                            BluetoothProfile.STATE_CONNECTED) {
                        Log.d(TAG, "onPreferenceClick,set LE1 to active");
                        mActivating = le_audioProfile.setActiveDevice(le1Device);
                        isSetActiveDeviceCalled = true;
                    }
                    if (!isSetActiveDeviceCalled &&
                            le_audioProfile.getConnectionStatus(le2Device) ==
                                    BluetoothProfile.STATE_CONNECTED ) {
                        Log.d(TAG, "onPreferenceClick,set LE2 to active");
                        mActivating = le_audioProfile.setActiveDevice(le2Device);
                    }
                }
                // USM ONLY scene
                if (SystemProperties.get("persist.vendor.bluetooth.leaudio_mode", "").
                        equalsIgnoreCase("ums")) {
                    Log.d(TAG,"USM ONLY scene,also set hfp to active");
                    BluetoothAdapter.getDefaultAdapter().setActiveDevice(device.getDevice(),
                            BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL);
                }
            } else {
                mActivating = device.setActive();
            }
                    //if device is active before set active, setActive will return true and will not broadcast
                    //and mActivating can not be reset to false, so reset here
                    if (isCurrentDeviceIsActive) mActivating = false;
                    return mActivating;
                }
        return (!mActivating);

    }

    @Override
    protected String getPreferenceKey() {
        return PREF_KEY;
    }

    private void checkLeAudioStatus() {
        // find all le audio devices
        // le audio address in grobal settins like
        // 74:A3:4A:E0:F0:11;CC:05:05:09:9A:D2;CA:07:68:45:4C:E3;
        String leAudioAddr = null;
        List<String> brAddress = new ArrayList<String>();
        List<String> le1Address = new ArrayList<String>();
        List<String> le2Address = new ArrayList<String>();
        try {
            leAudioAddr =  Settings.Global.getString(mContext.getContentResolver(),
                    "ble_audio_address");
            Log.d(TAG,"checkLeAudioStatus,leAudioAddr = " + leAudioAddr);
            if (leAudioAddr != null) {
                for (int i = 0;i < leAudioAddr.length() / (ADDRESS_LENGTH * 3);i++) {
                    String grpAddress = leAudioAddr.substring(ADDRESS_LENGTH * 3 * i,
                            ADDRESS_LENGTH * 3 * (i + 1));
                    brAddress.add(grpAddress.substring(0,ADDRESS_LENGTH - 1));
                    le1Address.add(grpAddress.substring(ADDRESS_LENGTH,ADDRESS_LENGTH * 2 - 1));
                    le2Address.add(grpAddress.substring(ADDRESS_LENGTH * 2,ADDRESS_LENGTH * 3 - 1));
                }
            }
        } catch (Exception e) {
            Log.v(TAG, "checkLeAudioStatus,Exception " + e);
        }

        for (int j = 0;j < brAddress.size();j++) {
            Log.d(TAG, "j = " + j + ",brStr = " + brAddress.get(j) + ",le1Str = " +
                    le1Address.get(j) + ",le1Str = " + le1Address.get(j));
            BluetoothDevice brDevice = BluetoothAdapter.getDefaultAdapter()
                    .getRemoteDevice(brAddress.get(j));
            if (brDevice.getBondState() == BluetoothDevice.BOND_BONDED &&
                    brDevice.getLeAudioStatus() == BluetoothDevice.LE_ADUIO_STATE_ON) {
                BluetoothDevice le1Device = BluetoothAdapter.getDefaultAdapter()
                        .getRemoteDevice(le1Address.get(j));
                BluetoothDevice le2Device = BluetoothAdapter.getDefaultAdapter()
                        .getRemoteDevice(le2Address.get(j));
                boolean le1ConnectableState = le1Device.isConnectableDevice();
                boolean le2ConnectableState = le2Device.isConnectableDevice();
                int le1BondState = le1Device.getBondState();
                int le2BondState = le2Device.getBondState();
                LeAudioProfile le_audioProfile = mLocalManager.getProfileManager().
                        getLeAudioProfile();
                int le1ConnectionState = BluetoothProfile.STATE_DISCONNECTED;
                int le2ConnectionState = BluetoothProfile.STATE_DISCONNECTED;
                if (le_audioProfile != null) {
                    le1ConnectionState = le_audioProfile.getConnectionStatus(le1Device);
                    le2ConnectionState = le_audioProfile.getConnectionStatus(le2Device);
                }
                Log.d(TAG, "le1Visiable = " + le1ConnectableState + ",le2Visiable = " +
                        le2ConnectableState + ",le1BondState = " + le1BondState +
                        ",le2BondState = " + le2BondState + ",le1ConnState = " +
                        le1ConnectionState + ",le2ConnState = " + le2ConnectionState);
                if (le1ConnectableState) {
                    if (le1BondState == BluetoothDevice.BOND_BONDING) {
                        Log.v(TAG, "checkLeAudioStatus,le1Device cancelBondProcess");
                        le1Device.cancelBondProcess();
                        continue;
                    } else if (le1BondState == BluetoothDevice.BOND_BONDED) {
                        if (le1ConnectionState == BluetoothProfile.STATE_DISCONNECTED) {
                            Log.v(TAG, "checkLeAudioStatus,le1Device remove bond");
                            le1Device.removeBond();
                            continue;
                        } else if (le1ConnectionState == BluetoothProfile.STATE_CONNECTING) {
                            Log.d(TAG, "checkLeAudioStatus,le1Device disconnect");
                            le_audioProfile.disconnect(le1Device);
                            continue;
                        } else if (le1ConnectionState == BluetoothProfile.STATE_CONNECTED) {
                            // le switch to active ,after this switch trigger le switch to null
                        }
                    }
                } else {
                    if (le2ConnectableState) {
                        if (le2BondState == BluetoothDevice.BOND_BONDING) {
                            Log.v(TAG, "checkLeAudioStatus,le2Device cancelBondProcess");
                            le2Device.cancelBondProcess();
                        } else if (le2BondState == BluetoothDevice.BOND_BONDED) {
                            if (le2ConnectionState == BluetoothProfile.STATE_DISCONNECTED) {
                                Log.v(TAG, "checkLeAudioStatus,le2Device remove bond");
                                le2Device.removeBond();
                            } else if (le1ConnectionState == BluetoothProfile.STATE_CONNECTING) {
                                Log.d(TAG, "checkLeAudioStatus,le2Device disconnect");
                                le_audioProfile.disconnect(le2Device);
                            } else if (le1ConnectionState == BluetoothProfile.STATE_CONNECTED) {
                                // le switch to active ,after this switch trigger le switch to null
                            }
                        }
                    }
                }
            }
        }
    }

}
